ConditionEnvironment#
- class ConditionEnvironment(cond_eval_function, args, kwargs={})[source]#
This class enables the usage of if-conditionals as we are used to from classical programming:
from qrisp import QuantumChar, QuantumFloat, h, multi_measurement q_ch = QuantumChar() qf = QuantumFloat(3, signed = True) h(q_ch[0]) with q_ch == "a": qf += 2
>>> print(multi_measurement([q_ch,qf])) {('a', 2): 0.5, ('b', 0): 0.5}
In this code snippet, we first bring the QuantumChar
q_ch
into the superposition\[\ket{\text{q_ch}} = \frac{1}{\sqrt{2}} \left( \ket{\text{"a"}} + \ket{\text{"b"}} \right)\]After that, we enter the ConditionEnvironment, which controls the operations on the condition that
q_ch
is in state \(\ket{a}\). Finally, we simultaneously measure both QuantumVariables. We see that the incrementation ofqf
only occured on the branch, whereq_ch
is equal to the character"a"
. The resulting quantum state is:\[\ket{\psi} = \frac{1}{\sqrt{2}} \left( \ket{\text{"a"}}\ket{2} + \ket{\text{"b"}}\ket{0} \right)\]It is furthermore possible to invert the condition truth value or apply phases. For this we acquire the QuantumBool containing the truth value using the as statement
from qrisp import x, p import numpy as np with q_ch == "a" as cond_bool: qf += 2 cond_bool.flip() qf -= 2 p(np.pi/4, cond_bool)
>>> qf.qs.statevector() sqrt(2)*(|a>*|4> + exp(I*pi/4)*|b>*|-2>)/2
Constructing custom conditional environments
Apart from infix notation like the equality operator, Qrisp also provides an interface for creating custom conditonal environments.
- Parameters:
- cond_eval_functionfunction
A function which evaluates the truth value. Must return a QuantumBool. Intermediate results do not have to be uncomputed or deleted, as this is automatically performed, when the condition truth value is uncomputed.
- argslist
The arguments on which to evaluate.
- kwargsdict, optional
A dictionary of keyword arguments for
cond_eval_function
. The default is {}.
Examples
We will now demonstrate how a ConditionEnvironment, that evaluates the equality of two QuantumVariables can be constructed:
from qrisp import QuantumBool, QuantumVariable, x, cx, mcx def quantum_eq(qv_0, qv_1): if qv_0.size != qv_1.size: raise Exception("Tried to evaluate equality condition for QuantumVariables of differing size") temp_qv = QuantumVariable(qv_0.size) cx(qv_0, temp_qv) cx(qv_1, temp_qv) x(temp_qv) res = QuantumBool() mcx(temp_qv, res) return res
In this function, we create a temporary variable where we apply CX gates controlled on the inputs onto. The qubits where
qv_0
andqv_1
agree, will then be in state \(\ket{0}\). After this, we apply regular X gates ontotemp_qv
such that the qubits where the inputs agree, are in state \(\ket{1}\). Finally, we apply a multi-controlled X gate onto the result to flip the result, if all of qubits of the qubits intemp_qv
are in the \(\ket{0}\) state.We inspect the resulting QuantumCircuit
>>> from qrisp import QuantumChar >>> q_ch_0 = QuantumChar() >>> q_ch_1 = QuantumChar() >>> res_bool = quantum_eq(q_ch_0, q_ch_1) >>> print(q_ch_0.qs)
QuantumCircuit: -------------- q_ch_0.0: ──■───────────────────────────────────────────────────────── │ q_ch_0.1: ──┼────■──────────────────────────────────────────────────── │ │ q_ch_0.2: ──┼────┼────■─────────────────────────────────────────────── │ │ │ q_ch_0.3: ──┼────┼────┼────■────────────────────────────────────────── │ │ │ │ q_ch_0.4: ──┼────┼────┼────┼────■───────────────────────────────────── │ │ │ │ │ q_ch_1.0: ──┼────┼────┼────┼────┼────■──────────────────────────────── │ │ │ │ │ │ q_ch_1.1: ──┼────┼────┼────┼────┼────┼────■─────────────────────────── │ │ │ │ │ │ │ q_ch_1.2: ──┼────┼────┼────┼────┼────┼────┼────■────────────────────── │ │ │ │ │ │ │ │ q_ch_1.3: ──┼────┼────┼────┼────┼────┼────┼────┼────■───────────────── │ │ │ │ │ │ │ │ │ q_ch_1.4: ──┼────┼────┼────┼────┼────┼────┼────┼────┼────■──────────── ┌─┴─┐ │ │ │ │ ┌─┴─┐ │ │ │ │ ┌───┐ temp_qv.0: ┤ X ├──┼────┼────┼────┼──┤ X ├──┼────┼────┼────┼──┤ X ├──■── └───┘┌─┴─┐ │ │ │ └───┘┌─┴─┐ │ │ │ ├───┤ │ temp_qv.1: ─────┤ X ├──┼────┼────┼───────┤ X ├──┼────┼────┼──┤ X ├──■── └───┘┌─┴─┐ │ │ └───┘┌─┴─┐ │ │ ├───┤ │ temp_qv.2: ──────────┤ X ├──┼────┼────────────┤ X ├──┼────┼──┤ X ├──■── └───┘┌─┴─┐ │ └───┘┌─┴─┐ │ ├───┤ │ temp_qv.3: ───────────────┤ X ├──┼─────────────────┤ X ├──┼──┤ X ├──■── └───┘┌─┴─┐ └───┘┌─┴─┐├───┤ │ temp_qv.4: ────────────────────┤ X ├────────────────────┤ X ├┤ X ├──■── └───┘ └───┘└───┘┌─┴─┐ res.0: ───────────────────────────────────────────────────────┤ X ├ └───┘ Live QuantumVariables: --------------------- QuantumChar q_ch_0 QuantumChar q_ch_1 QuantumVariable temp_qv QuantumBool res
We can now construct the conditional environment from this function
from qrisp import ConditionEnvironment, multi_measurement, h #Create some sample arguments on which to evaluate the condition q_bool_0 = QuantumBool() q_bool_1 = QuantumBool() q_bool_2 = QuantumBool() h(q_bool_0) with ConditionEnvironment(cond_eval_function = quantum_eq, args = [q_bool_0, q_bool_1]): q_bool_2.flip()
>>> print(multi_measurement([q_bool_0, q_bool_1, q_bool_2])) {(False, False, True): 0.5, (True, False, False): 0.5}
This agrees with our expectation, that
q_bool_2
is onlyTrue
if the other two agree.The quantum_condition decorator
Creating quantum conditions like this seems a bit unwieldy. For a more convenient solution, we provide the
quantum_condition
decorator. This decorator can be applied to a function returning a QuantumBool, which is then returning the corresponding ConditionEnvironment instead. To demonstrate, we construct a “less than” condition for QuantumFloatsfrom qrisp import quantum_condition, cx @quantum_condition def less_than(qf_0, qf_1): temp_qf = qf_0 - qf_1 res = QuantumBool() cx(temp_qf.sign(), res) return res qf_0 = QuantumFloat(3) qf_1 = qf_0.duplicate() qf_0[:] = 2 h(qf_1[:2]) res_q_bool = QuantumBool() with less_than(qf_0, qf_1): res_q_bool.flip()
>>> print(multi_measurement([qf_0, qf_1, res_q_bool])) {(2, 0, False): 0.25, (2, 1, False): 0.25, (2, 2, False): 0.25, (2, 3, True): 0.25}
Quantum-Loops
An interesting application of conditional environments is the
qRange
iterator. Using this construct, we can mimic a loop as we are used from classical computing, where the end of the loop is determined by a quantum state:from qrisp import QuantumFloat, qRange, h n = QuantumFloat(3) qf = QuantumFloat(5) n[:] = 4 h(n[0]) n_results = n.get_measurement() for i in qRange(n): qf += i
>>> print(qf) {10: 0.5, 15: 0.5}
This script calculates the sum of all integers up to a certain threshold. The threshold (n) is a QuantumFloat in superposition, implying the result of the sum is also in a superposition. The expected results can be quickly determined by using Gauß’s formula:
\[\sum_{i = 0}^n i = \frac{n(n+1)}{2}\]>>> print("Excpected outcomes:", [n*(n+1)/2 for n in n_results.keys()]) Excpected outcomes: [10.0, 15.0]